﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace LowLevelGraphics.ColorSpaces
{
    /// <summary>
    /// Colorstruct to be able to use float values for each channel
    /// </summary>
    public struct ColorStructF
    {
        public float R;
        public float G;
        public float B;

        /// <summary>
        /// specify channel values directly
        /// </summary>
        /// <param name="_R"></param>
        /// <param name="_G"></param>
        /// <param name="_B"></param>
        public ColorStructF(float _R, float _G, float _B)
        {
            R = _R;
            G = _G;
            B = _B;
        }

        /// <summary>
        /// Convert back to rgb color
        /// </summary>
        /// <returns></returns>
        public Color ToColor()
        {
            return Color.FromArgb((int)R, (int)G, (int)B);
        }

        /// <summary>
        /// Gets or sets the R.
        /// </summary>
        /// <value>The R.</value>
        public float Red
        {
            get { return R; }
            set { R = value; }
        }

        /// <summary>
        /// Gets or sets the G.
        /// </summary>
        /// <value>The G.</value>
        public float Green
        {
            get { return G; }
            set { G = value; }
        }

        /// <summary>
        /// Gets or sets the B.
        /// </summary>
        /// <value>The B.</value>
        public float Blue
        {
            get { return B; }
            set { B = value; }
        }
    }

    /// <summary>
    /// Color struct
    /// </summary>
    public struct ColorStruct
    {
        public int R;
        public int G;
        public int B;

        /// <summary>
        /// specify color directly
        /// </summary>
        /// <param name="_color"></param>
        public ColorStruct(Color _color)
        {
            R = _color.R;
            G = _color.G;
            B = _color.B;
        }

        /// <summary>
        /// specify values for each channel directly
        /// </summary>
        /// <param name="_R"></param>
        /// <param name="_G"></param>
        /// <param name="_B"></param>
        public ColorStruct(int _R, int _G, int _B)
        {
            R = _R;
            G = _G;
            B = _B;     
        }

        /// <summary>
        /// Toes the color.
        /// </summary>
        /// <returns></returns>
        public Color ToColor()
        {
            return Color.FromArgb(R,G,B);
        }

        /// <summary>
        /// Correct limits, i.e. eliminate values > 255 or smaller than 0
        /// </summary>
        public void CorrectLimits()
        {
            if (R < 0) R = 0;
            if (R > 255) R = 255;

            if (G < 0) G = 0;
            if (G > 255) G = 255;

            if (B < 0) B = 0;
            if (B > 255) B = 255;
        }

        /// <summary>
        /// Gets or sets red
        /// </summary>
        public int Red
        {
            get { return R; }
            set { R = value; }
        }

        /// <summary>
        /// Gets or sets green
        /// </summary>
        public int Green
        {
            get { return G; }
            set { G = value; }
        }

        /// <summary>
        /// Gets or sets blue
        /// </summary>
        public int Blue
        {
            get { return B; }
            set { B = value; }
        }
    }

    /// <summary>
    /// This is the base class for creating color spaces
    /// </summary>
    public abstract class AbstractColorSpace : ICloneable
    {
        protected Color m_Color = Color.Transparent;

        /// <summary>
        /// Used to calculate new channel values from rgb
        /// </summary>
        protected abstract void CalcValues();

        /// <summary>
        /// Override this to implement retransformation of concrete color space
        /// back to rgb
        /// </summary>
        /// <returns></returns>
        public abstract Color ToColor();

        /// <summary>
        /// Default constructor
        /// </summary>
        public AbstractColorSpace()
        {
        }

        /// <summary>
        /// primary constructor that should be used
        /// </summary>
        /// <param name="_color"></param>
        public AbstractColorSpace(Color _color)
        {
            m_Color = _color;
            CalcValues();
        }

        /// <summary>
        /// Set color
        /// </summary>
        public Color Color
        {
            set
            {
                m_Color = value;
                CalcValues();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public virtual AbstractColorSpace Normalize()
        {
            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public virtual Color NormalizeToRGBColor()
        {
            return ColorFunctions.NormalizeToColor(this.ToColor());
        }

        /// <summary>
        /// Gets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get { return this.GetType().Name; }
        }

        #region ICloneable Member

        /// <summary>
        /// Used to create a deep copy
        /// </summary>
        /// <returns></returns>
        public virtual object Clone()
        {
            //Because this is the abstract class it has to be like this here....
            throw new NotImplementedException();
        }

        /// <summary>
        /// Gets the values.
        /// </summary>
        /// <returns></returns>
        public abstract object[] GetValues();
        
        /// <summary>
        /// Sets the values.
        /// </summary>
        /// <param name="_aObject">The _a object.</param>
        public abstract void SetValues(object[] _aObject);
        #endregion
    }
}
